/**
 * Copyright (c) 2017-2018, zengjintao (1913188966@qq.com).
 * <p>
 * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.gnu.org/licenses/lgpl-3.0.txt
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.jfast.framework.kit;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.*;

import javax.sql.DataSource;

import com.google.common.collect.Lists;

/**
 * model代码生成工具类
 */
public final class ModelKit {
	
	private String packageName;
	
	private DataSource dataSource;
	private final TypeHandler typeHandler = new TypeHandler();
	public static final String DEFAULT_PATH = "/src/main/java/";
	private static final List<String> tables = Lists.newArrayList();
	
	public ModelKit(String packageName) {
		this.packageName = DEFAULT_PATH + packageName;
	}
	
	public ModelKit(String packageName, DataSource dataSource) {
		this.packageName = DEFAULT_PATH + packageName;
		this.dataSource = dataSource;
	}
	
	public ModelKit(String src, String packageName) {
		this.packageName = src + packageName;
	}
	
	public void create() {
		long start = System.currentTimeMillis();
		try {
			Connection connection = dataSource.getConnection();
			DatabaseMetaData  databaseMetaData = connection.getMetaData();
			ResultSet resultSet = databaseMetaData.getTables(null, null, null, null);
			while (resultSet.next()) {
				tables.add(resultSet.getString("TABLE_NAME"));
			}
			for (Iterator<String> iterator = tables.iterator(); iterator.hasNext();) {
				String tableName = iterator.next();
				PreparedStatement pt = connection.prepareStatement("select * from "+tableName);
			    ResultSetMetaData metaData = pt.getMetaData();
			    int count = metaData.getColumnCount();
			    String[] columnNames = new String[count];
			    String[] typeName = new String[count];
			    int[] columnDisplaySize = new int[count];
		        for (int i = 0; i < count; i++) {
		        	String columnName = metaData.getColumnName(i + 1);
		        	if (columnName.contains("_")) {
		        		String[] columnNameArrays = columnName.split("_");
		        		columnName = "";
		        		for (int j = 0; j < columnNameArrays.length; j++) {
		        			columnName += j == 0 ? columnNameArrays[j] : StrKit.toUpperCaseFirst(columnNameArrays[j]);
						}
			      	    columnNames[i] = columnName;//列名
		        	} else {
		        		columnNames[i] = metaData.getColumnName(i + 1);//列名
		        	}
			      	typeName[i] = metaData.getColumnTypeName(i + 1);//列类型
			      	columnDisplaySize[i] = metaData.getColumnDisplaySize(i+1);
			    }
		        String newTableName = getNewTableName(tableName);
				String context = createModel(columnNames, typeName, columnDisplaySize, newTableName);
				generateModel(context,newTableName);
		    }
			long end = System.currentTimeMillis();
			System.out.println("生成model成功,耗时"+(end-start)+"ms");
		} catch(Exception e) {
			e.printStackTrace();
			System.err.println("生成model异常");
		}
	}

	/**
	 * 将类写入文件
	 * @param context
	 */
	private void generateModel(String context,String tableName) {
		FileWriter filterWriter = null;
		try {
			String filePath = PathKit.getWebRootPath()+ packageName.replace(".", "\\");
			File file = new File(filePath);
			if (!file.exists()) {
				file.mkdirs();
			}
			File source = new File(file,StrKit.toUpperCaseFirst(tableName) + ".java");
			if (!source.exists()) {
				source.createNewFile();
			}
			filterWriter = new FileWriter(source);
			filterWriter.write(context);
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				filterWriter.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	private  String createModel(String[] columnName, String[] typeName, int[] columnDisplaySize, String tableName) {
		StringBuffer stringBuffer = new StringBuffer();
		StringBuffer packBuffer = new StringBuffer();
		generatePackage(packBuffer);
		stringBuffer.append("public class  " + StrKit.toUpperCaseFirst(tableName) + "{\n\n");
		generFiled(stringBuffer,columnName,columnDisplaySize,typeName);
		for (int i = 0; i < columnName.length; i++) {
			stringBuffer.append("\tpublic void set" + StrKit.toUpperCaseFirst(columnName[i]) + "("+getType(typeName[i],columnDisplaySize[i])+" "+columnName[i]+"){");
			stringBuffer.append("\n\t\t"+"this." + columnName[i] + " = "+columnName[i] + ";\n\t}\n\n");
			stringBuffer.append("\tpublic  " + getType(typeName[i],columnDisplaySize[i]));
			String type = getType(typeName[i],columnDisplaySize[i]);
			
			if (type.equals("boolean") || type.equals("Boolean")) {
				stringBuffer.append("  is");
			} else {
				stringBuffer.append("  get");
			}
			stringBuffer.append(StrKit.toUpperCaseFirst(columnName[i]) + "(){");
			stringBuffer.append("\n\t\t" + "return\t" + columnName[i] + ";\n\t}\n");
		}
		List<String> backTypeName = Arrays.asList(typeName);
		if (backTypeName.contains("DATETIME")) {
			packBuffer.append("import java.util.Date;\n\n");
		}
		if (backTypeName.contains("DECIMAL")) {
			packBuffer.append("import java.math.BigDecimal;\n");
		}
		stringBuffer.append("}");
		return  packBuffer.toString() + stringBuffer.toString();
	}

	/**
	 * 生成属性
	 * @param stringBuffer
	 * @param columnName
	 * @param typeName
	 */
	private void generFiled(StringBuffer stringBuffer, String[] columnName,int[] columnDisplaySize, String[] typeName) {
		for (int i = 0; i < columnName.length; i++) {
			stringBuffer.append("\tprivate  " + getType(typeName[i],columnDisplaySize[i]) + "  " 
		       + columnName[i] + ";\n\n");
		}
	}

	private void generatePackage(StringBuffer packBuffer) {
		packBuffer.append("package "+ packageName.replace("/src/main/java/", "") + ";\n\n");
	}

	/**
	 * 获取数据库表字段类型
	 * @param typeName
	 * @return
	 */
	private String getType(String typeName, int size) {
		if (typeName.equals("TINYINT") || typeName.equals("SMALLINT")) {
			if (size == 1) {
				return "boolean";
			}
			return "int";
		}
		return typeHandler.getType(typeName);
	}
	
	private String getNewTableName(String tableName) {
		if (tableName.contains("_")) {
			String[] tableNameArry = tableName.split("_");
			String newTableName = "";
			for (int i = 0; i < tableNameArry.length; i++) {
				newTableName += StrKit.toUpperCaseFirst(tableNameArry[i]);
			}
			return newTableName;
		}
		return tableName;
	}

	private class TypeHandler {

		private final Map<String, String> typeMap = new HashMap<>();

		TypeHandler() {
			typeMap.put("VARCHAR", "String");
			typeMap.put("NVARCHAR", "String");
			typeMap.put("DATETIME", "Date");
			typeMap.put("MONEY", "double");
			typeMap.put("INT", "int");
			typeMap.put("DECIMAL", "BigDecimal");
			typeMap.put("FLOAT", "float");
			typeMap.put("DOUBLE", "double");
			typeMap.put("BIGINT", "long");
			typeMap.put("BIT", "long");
		}

		String getType(String key) {
			return typeMap.get(key);
		}
	}
}
